package com.onlyxiahui.common.action.description.handler.impl;

import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import org.springframework.core.GenericTypeResolver;
import org.springframework.core.MethodParameter;
import org.springframework.core.annotation.SynthesizingMethodParameter;

import com.onlyxiahui.common.action.description.DocumentContext;
import com.onlyxiahui.common.action.description.bean.MethodData;
import com.onlyxiahui.common.action.description.bean.ModuleData;
import com.onlyxiahui.common.action.description.bean.ResultData;
import com.onlyxiahui.common.action.description.handler.MethodHandler;
import com.onlyxiahui.common.action.description.handler.MethodTitleHandler;
import com.onlyxiahui.common.action.description.handler.ResultHandler;
import com.onlyxiahui.common.action.description.util.ActionKeyUtil;
import com.onlyxiahui.common.action.description.util.ActionMd5Util;
import com.onlyxiahui.common.action.description.util.ActionTitleUtil;
import com.thoughtworks.qdox.model.JavaMethod;

/**
 * Description <br>
 * Date 2020-01-07 16:17:59<br>
 * 
 * @author XiaHui [onlovexiahui@qq.com]<br>
 * @since 1.0.0
 */

public abstract class BaseMethodHandler implements MethodHandler {

	/**
	 * 
	 * Description <br>
	 * Date 2020-04-10 14:15:15<br>
	 * 
	 * @param context
	 * @param moduleData
	 * @param method
	 * @param paths
	 * @param combineAction 是否合并路径
	 * @return
	 * @since 1.0.0
	 */
	public MethodData create(
			DocumentContext context,
			ModuleData moduleData,
			Method method,
			List<String> paths,
			boolean combineAction) {

		Class<?> moduleClass = method.getDeclaringClass();
		String methodName = method.getName();
		Parameter[] parameters = method.getParameters();

		String key = "";
		String name = methodName;
		String title = getMethodTitle(context, moduleData, method);
		// String action = "";
		String description = getMethodDescription(context, method);
		String className = moduleClass.getName();

		MethodData md = new MethodData();

		String pk = ActionKeyUtil.getKey(parameters);
		key = className + "." + methodName + "_" + pk;

		md.setKey(ActionMd5Util.lower32(key));
		md.setPaths(paths);
		md.setName(name);
		md.setClassName(className);
		md.setTitle(title);
		md.setDescription(description);

		if (combineAction) {
			List<String> modulePaths = moduleData.getPaths();
			Set<String> actions = combineAction(context, modulePaths, paths);
			md.setActions(actions);
		}

		for (ResultHandler h : context.getResultHandlers()) {
			if (h.support(context, moduleData, md, method)) {
				ResultData rd = h.handle(context, moduleData, md, method);
				if (null != rd) {
					md.setResult(rd);
					break;
				}
			}
		}
		return md;
	}

	public Set<String> combineAction(DocumentContext context, List<String> modulePaths, List<String> methodPaths) {
		if (null == modulePaths) {
			modulePaths = new ArrayList<>();
		}
		if (null == methodPaths) {
			methodPaths = new ArrayList<>();
		}

		Set<String> actions = new HashSet<>();

		if (modulePaths.isEmpty()) {
			for (String methodPath : methodPaths) {
				String path = context.getPathMatcher().combine("", methodPath);
				if (null != path && !path.isEmpty()) {
					actions.add(path);
				}
			}
		} else {
			for (String modulePath : modulePaths) {
				for (String methodPath : methodPaths) {
					String path = context.getPathMatcher().combine(modulePath, methodPath);
					if (null != path && !path.isEmpty()) {
						actions.add(path);
					}
				}
			}
		}
		return actions;
	}

	protected MethodParameter[] initMethodParameters(Method method, Class<?> clazz) {
		int count = method.getParameterCount();
		MethodParameter[] result = new MethodParameter[count];
		for (int i = 0; i < count; i++) {
			SynthesizingMethodParameter parameter = SynthesizingMethodParameter.forExecutable(method, i);
			GenericTypeResolver.resolveParameterType(parameter, clazz);
			result[i] = parameter;
		}
		return result;
	}

	protected List<ActionMethod> getActionMethodList(Class<?> clazz) {
		List<ActionMethod> list = new ArrayList<>();
		Method[] methods = clazz.getMethods();
		if (null != methods && methods.length > 0) {
			for (Method method : methods) {
				list.add(new ActionMethod(clazz, method));
			}
		}
		return list;
	}

	public String getMethodTitle(DocumentContext context, ModuleData moduleData, Method method) {
		Class<?> moduleClass = method.getDeclaringClass();
		String methodName = method.getName();
		String className = moduleClass.getName();
		Parameter[] parameters = method.getParameters();

		JavaMethod jm = context.getSourceBox().getJavaMethodByName(className, methodName, parameters);
		String comment = "";
		if (null != jm) {
			comment = jm.getComment();
		}
		String title = null;

		for (MethodTitleHandler h : context.getMethodTitleHandlers()) {
			if ((null == title || title.isEmpty()) && h.support(context, moduleData, method, comment)) {
				title = h.handle(context, moduleData, method, comment);
			}
		}

		if (null == title || title.isEmpty()) {
			title = ActionTitleUtil.handle(comment);
		}

		if (null == title || title.isEmpty()) {
			title = methodName;
		}
		return null == title ? "" : title;
	}

	public String getMethodDescription(DocumentContext context, Method method) {
		Class<?> moduleClass = method.getDeclaringClass();
		String methodName = method.getName();
		String className = moduleClass.getName();
		Parameter[] parameters = method.getParameters();

		String description = "";
		JavaMethod jm = context.getSourceBox().getJavaMethodByName(className, methodName, parameters);
		if (null != jm) {
			description = jm.getComment();
		}
		return null == description ? "" : description;
	}

	protected class ActionMethod {

		private Class<?> actionClass;
		private Method method;
		private MethodParameter[] methodParameters;

		public ActionMethod(Class<?> actionClass, Method method) {
			this.actionClass = actionClass;
			this.method = method;
			initialize(method);
		}

		private void initialize(Method method) {
			methodParameters = initMethodParameters(method);
		}

		private MethodParameter[] initMethodParameters(Method method) {
			int count = method.getParameterCount();
			MethodParameter[] result = new MethodParameter[count];
			for (int i = 0; i < count; i++) {
				SynthesizingMethodParameter parameter = SynthesizingMethodParameter.forExecutable(method, i);
				GenericTypeResolver.resolveParameterType(parameter, this.actionClass);
				result[i] = parameter;
			}
			return result;
		}

		public Class<?> getActionClass() {
			return actionClass;
		}

		public Method getMethod() {
			return method;
		}

		public MethodParameter[] getMethodParameters() {
			return methodParameters;
		}
	}
}
